home *** CD-ROM | disk | FTP | other *** search
/ Collection of Tools & Utilities / Collection of Tools and Utilities.iso / tex / refs51.zip / REFS51.PAS < prev   
Encoding:
Pascal/Delphi Source File  |  1989-11-06  |  18.0 KB  |  639 lines

  1. {$R-}    {Range checking off}
  2. {$B-}    {Boolean short circuiting off}
  3. {$S+}    {Stack checking on}
  4. {$I+}    {I/O checking on}
  5. {$N-}    {No numeric coprocessor}
  6.  
  7. program refs;
  8.  
  9.     (*REFS-- find and list references in manuscripts
  10.  
  11.   COPYRIGHT 1985 by Ross A. Alford
  12.   All commercial rights reserved.  This software is released for
  13.   nonprofit distribution only.    Any commercial distribution should be
  14.   undertaken only with the express consent of the copyright holder:
  15.  
  16.             Ross A. Alford
  17.             Department of Zoology
  18.             Duke University
  19.             Durham, NC 27706
  20.             ...[decvax, ihnp4, akgua]!mcnc!ecsvax!alford
  21.  
  22.   REFS finds references in scientific manuscripts.  It will list references
  23.   found and the number of times they are occur to a file, a printer, or
  24.   the system console.  It  should work with references of the forms:
  25.  
  26.    Smith, 1980                   |Smith (1980)
  27.      Smith, 1980a                  |Smith (1980a)
  28.      Smith, 1980a, b               |Smith (1980a, b)
  29.      Smith, 1980a, 1980b           |Smith (1980a, 1980b)
  30.      Smith and Smith, 1980         |Smith and Smith (1980)
  31.      Smith et al., 1980            |Smith et al. (1980)
  32.      Smith's 1980                  |Smith's (1980)
  33.      Smith, Smith, and Smith, 1980 |Smith, Smith, and Smith (1980)
  34.      Smith-Smythe and Smith 1980
  35.  
  36.      Smith {\it et al.} (1980)            ( Added by JM-M, see below )
  37.      \fnote{Smith (1980)}           (           ditto          )
  38.  
  39.   and with most any similar style.  It also allows the last digit of the year
  40.   to be replaced by a letter, as Smith, 198x, for cases where the exact date
  41.   is uncertain.  It may not work entirely properly on references in tabular
  42.   formats, specifically if a reference of the form Smith 1980a,b is split
  43.   between lines so that the 'b' is widely separated from the 'a'.
  44.  
  45.   Month, year dates, as July, 1980, also are treated as references.  You never
  46.   know when some person might have the same name as a month.
  47.  
  48.   Operating the program is simple, and is documented in the msgexit function.
  49.   Just run the program with no parameters for a description.  I apologize for
  50.   the paucity of comments, but after all this is self-documenting Pascal :-)
  51.  
  52.   Please let me know of any bugs found, bug fixes made, or improvements
  53.   made.
  54.  
  55.     Ross Alford*)
  56.  
  57. (* REVISED
  58.  
  59.   Version:       1.5j
  60.   Revisions by:  Jeff_MacKie-Mason@um.cc.umich.edu
  61.                  Dept. of Economics
  62.                  Univ. of Michigan
  63.                  Ann Arbor, MI 48109
  64.  
  65.   Revision date: 18 November 1987
  66.                                 6 November 1989
  67.  
  68.   Revision abstract:
  69.      1) Upgraded for compilation with Turbo Pascal v. 4.0.
  70.           Actually, I used the Turbo3 standard unit, so version 3.0
  71.           definitions are mostly in effect, but compiling with v. 4.0
  72.           leads to a smaller executable.
  73.  
  74.           Had to change DOS device names, and add a result parameter
  75.           to blockread to handle incomplete reads.
  76.  
  77.      2) Rewrote GetArg function to use ParamStr (especially since
  78.                     it is illegal to use CSeg in an absolute declaration)
  79.  
  80.          3) Changed MemAvail to longint, converted returned bytes to paragraphs,
  81.                 deleted reference to Turbo3 unit, recompiled with Turbo5. (11/6/89)
  82.  
  83.          4) Modified logic to recognize refs that begin \fnote's,
  84.                 and refs of the form Baker {\it et al.} (1980) in TeX. (11/6/89)
  85.  
  86.          5) Modified logic to recognize \footnote and refs of form
  87.                 Baker {\em et al.} (1980) for LaTex.  (11/7/89)
  88. *)
  89.  
  90. Uses
  91.     Crt;
  92.  
  93. const charsect = 128;
  94.       namelen = 60;
  95.             version = '1.3';
  96.             jversion = '5.1j';
  97.  
  98. type fnamestr = string[14];
  99.      msgstr = string[80];
  100.      tabletyp = array[0..127] of boolean;
  101.      buftype = array[1..CHARSECT] of byte;
  102.      nametyp = string[NAMELEN];
  103.      datetyp = string[5];
  104.      refptr = ^reference;
  105.      reference = record
  106.            next : refptr;
  107.            name : string[NAMELEN];
  108.            number : integer
  109.          end;
  110.      sectptr = ^sectrec;
  111.      sectrec = record
  112.         next : sectptr;
  113.         previous : sectptr;
  114.         data : buftype
  115.           end;
  116.  
  117. var inf : file;
  118.     i,j,ptrsave,sinceref : integer;
  119.     c : byte;
  120.     oldyear,year : datetyp;
  121.     xtra,name,tempname,oldname : nametyp;
  122.     closeparen,notfound : boolean;
  123.     outfname,infile : fnamestr;
  124.     reflist : refptr;
  125.     result : word;                             { new in v.1.3j}
  126.  
  127. {intentional global variables- to speed things up}
  128.  
  129.     outf : text;
  130.     lowcase,isupcase,otherbad,letter,number : tabletyp;
  131.     cursectnum,numinfile : integer;
  132.     infopen,outfopen,hitnumber,comma,done : boolean;
  133.     sector,savesect : sectptr;        {current sector in use}
  134.     ptr,saveptr : integer;        {location within sector}
  135.  
  136.  
  137. {---------exit gracefully with info---------------------------------------}
  138.  
  139. procedure msgexit(msg : msgstr);
  140.  
  141. begin
  142.   if infopen then close(inf);
  143.   if outfopen then close(outf);
  144.   writeln;
  145.   if msg <> '' then
  146.     begin
  147.       writeln(chr(7),msg);
  148.       writeln
  149.     end;
  150.   writeln('REFS finds references in the name, date form in manuscripts.');
  151.   writeln;
  152.   writeln('To run REFS enter a command line like:');
  153.   writeln;
  154.   writeln('A>refs infile {outfile}');
  155.   writeln;
  156.   writeln('Where infile is a DOS filename of the form drive:filename.ext');
  157.   writeln;
  158.   writeln('and outfile can be either a disk file, given in the same format ');
  159.   writeln('as infile, or can be given as CON to send output to the CRT screen');
  160.   writeln('or LPT1 to send output to the DOS list device.');
  161.   writeln;
  162.   writeln('If outfile is not specified, a file of the same base name as infile');
  163.   writeln('but with the extension .REF, will be created on the same drive that');
  164.   writeln('infile is read from.');
  165.   writeln;
  166.   halt
  167. end;
  168.  
  169.  
  170. {---------------------read a sector into a sector buffer-------------------}
  171.  
  172. procedure readsector(var sector : sectptr);
  173.  
  174. begin
  175.   if cursectnum < numinfile then
  176.     begin
  177.       blockread(inf,sector^.data,1);
  178.       cursectnum := succ(cursectnum)
  179.     end
  180.     else done := TRUE
  181. end;
  182.  
  183.  
  184. {------------------------get a new sector buffer node-------------------}
  185.  
  186. procedure getsectnode(var sector : sectptr);
  187.  
  188. var n : longint;
  189.  
  190. begin
  191.     n := memavail div 16;
  192.   if ((n and $7fff) < 512) then msgexit('Out of memory during sector read');
  193.   new(sector);
  194.   sector^.next := NIL;
  195.   sector^.previous := NIL
  196. end;
  197.  
  198.  
  199. {----------------return the character currently pointed to-------------}
  200.  
  201. function curbyte : byte;           {uses globals sector and ptr}
  202.  
  203. begin
  204.   curbyte := sector^.data[ptr] and $7f
  205. end;
  206.  
  207.  
  208. {---------get next character, read a new sector if needed--------------}
  209.  
  210. function nextbyte : byte;           {uses globals sector and ptr}
  211.  
  212. var tempsec : sectptr;
  213.     t : byte;
  214.  
  215. begin
  216.   ptr := succ(ptr);
  217.   if ptr > 128 then
  218.     if sector^.next = NIL then
  219.       begin
  220.     tempsec := sector^.previous;
  221.     if tempsec = NIL then getsectnode(tempsec);
  222.     readsector(tempsec);
  223.     if not done then
  224.       begin
  225.         tempsec^.previous := sector;
  226.         sector^.previous := NIL;
  227.         sector^.next := tempsec;
  228.         tempsec^.next := NIL;
  229.         sector := tempsec;
  230.         ptr := 1
  231.       end
  232.       end
  233.       else begin
  234.     tempsec := sector^.next;
  235.     tempsec^.next := NIL;
  236.     tempsec^.previous := sector;
  237.     sector^.previous := NIL;
  238.     sector := tempsec;
  239.     ptr := 1;
  240.       end;
  241.   t := sector^.data[ptr];
  242.   if t = 26 then done := TRUE;
  243.   nextbyte := t and $7f
  244. end;
  245.  
  246.  
  247. {--------------------return previous character--------------------}
  248.  
  249. function prevbyte : byte;           {uses globals sector and ptr}
  250.  
  251. var tempsec : sectptr;
  252.     ch : byte;
  253.  
  254. begin
  255.   ptr := pred(ptr);
  256.   if ptr < 1 then
  257.     begin
  258.       if sector^.previous <> NIL then
  259.     begin
  260.       tempsec := sector^.previous;
  261.       tempsec^.next := sector;
  262.       tempsec^.previous := NIL;
  263.       sector^.next := NIL;
  264.       sector := tempsec;
  265.       ptr := 128
  266.     end
  267.     end;
  268.   if (ptr < 1) then
  269.     prevbyte := 0
  270.     else prevbyte := sector^.data[ptr] and $7f
  271. end;
  272.  
  273.  
  274. {return previous alphabetic word.  Set the global 'comma'=TRUE if a comma
  275.   follows it.  Set the global 'hitnumber' TRUE if a digit is encountered.
  276.   Return no word if any of the characters for which corresponding entries
  277.   in the tables 'otherbad' or 'number' have been set true is encountered.}
  278.  
  279. function prevword : nametyp;
  280.  
  281. var c,d : byte;
  282.     i : integer;
  283.     gotalet : boolean;
  284.     name : nametyp;
  285.  
  286. begin
  287.   i := 0;
  288.   comma := FALSE;
  289.   gotalet := FALSE;
  290.   hitnumber := FALSE;
  291.   name := '';
  292.   repeat
  293.     c := prevbyte;
  294.     i := succ(i);
  295.     if (c = ord(',')) then comma := TRUE;
  296.     if letter[c] then
  297.       begin
  298.     if (not number[prevbyte]) then
  299.       gotalet := TRUE;
  300.     d := nextbyte                 {readjust pointer}
  301.       end;
  302.     if number[c] then hitnumber := TRUE;
  303.     if otherbad[c] then i := 126
  304.   until gotalet or (i = 126);
  305.   if gotalet then
  306.     while letter[c] do
  307.       begin
  308.     name := chr(c) + name;
  309.     c := prevbyte
  310.       end;
  311.   c := nextbyte;
  312.   prevword := name
  313. end;
  314.  
  315.  
  316. {--------------save position in file before backwards scan-----------}
  317.  
  318. procedure saveposition;
  319.  
  320. begin
  321.   savesect := sector;
  322.   saveptr := ptr
  323. end;
  324.  
  325.  
  326. {------------restore position in file after backwards scan------------}
  327.  
  328. procedure restoreposition;
  329.  
  330. begin
  331.   sector := savesect;
  332.   ptr := saveptr
  333. end;
  334.  
  335.  
  336. {Set up truth tables for membership in sets of characters.  Indexing into
  337.  these tables is much faster than using the standard set notation.}
  338.  
  339. procedure inittables;
  340.  
  341. var i : integer;
  342.  
  343. begin
  344.   for i := 0 to 127 do
  345.     begin
  346.       letter[i] := (((i > $40) and (i < $5b)) or ((i > $60) and (i < $7b)));
  347.       number[i] := ((i >= ord('0')) and (i <= ord('9')));
  348.       isupcase[i] := ((i > $40) and (i < $5b));
  349.       lowcase[i] := ((i > $60) and (i < $7b));
  350.       otherbad[i] := FALSE;
  351.       if chr(i) in ['=','<','>',':'] then otherbad[i] := TRUE
  352.     end;
  353.     letter[39] := TRUE;                  {apostrophe is a letter}
  354.     letter[ord('{')] := TRUE;        {open curly bracket is a letter, for TeX: JMM}
  355.     letter[ord('\')] := TRUE;        {so is backslash: JMM}
  356.     letter[ord('-')] := TRUE;        {so is hyphen}
  357. end;
  358.  
  359.  
  360. {Write the accumulated linked list of references onto the defined output file}
  361.  
  362. procedure writelist(var outf : text; list : refptr);
  363.  
  364. var current : refptr;
  365.     totcites,totrefs : integer;
  366.  
  367. begin
  368.   totcites := 0;
  369.   totrefs := 0;
  370.   writeln(outf,'References from file ',infile);
  371.   writeln(outf);
  372.   writeln(outf,'Author(s) and date; number of times cited');
  373.   writeln(outf);
  374.   current := list^.next;
  375.   while current <> NIL do
  376.     begin
  377.       writeln(outf,current^.name,';  ',current^.number);
  378.       totrefs := succ(totrefs);
  379.       totcites := totcites + current^.number;
  380.       current := current^.next
  381.     end;
  382.   writeln(outf);
  383.   writeln(outf,'Total count of citations in text = ',totcites);
  384.   writeln(outf,'Total number of references cited = ',totrefs);
  385.   close(outf)
  386. end;
  387.  
  388.  
  389. {-------------return an initialized storage node for a reference-----------}
  390.  
  391. procedure getrefnode(var x : refptr);
  392.  
  393. var i : longint;
  394.  
  395. begin
  396.     i := memavail div 16;
  397.   if ((i > 0) and (i < 512)) then
  398.       msgexit('Out of memory--too many references--try splitting input file');
  399.   new(x);
  400.   x^.next := NIL;
  401.   x^.name := 'A';
  402.   x^.number := 1
  403. end;
  404.  
  405. {-------------------------------------------------------------------
  406. function getarg reads a series of characters from the DOS command line buffer.
  407. It returns everything up to the next space it encounters and saves what's left
  408. of the buffer.    If there's nothing left, it returns the empty string.  This
  409. function is VERY Turbo-specific: it relies on static variable allocation
  410. to preserve the command string between calls}
  411.  
  412. (*function getarg : fnamestr;        { commented out to upgrade to TP4 }
  413.  
  414. const called : boolean = FALSE;          {used while debugging only}
  415.       i : integer = 1;
  416.       j : integer = 1;
  417.  
  418. {var cmdbuf : string[127] absolute $80;}            {to run after compiling to
  419.                                                    memory, comment out the
  420.                                                   'absolute $80'}
  421.  
  422. var cmdbuf : string[127] absolute CSeg:$80;     {for MS/PC DOS command-line}
  423.  
  424. begin
  425.  
  426.   if not called then                 {used for debugging}
  427.     begin                     {with memory compilation}
  428.       write('Enter command line: ');
  429.       readln(cmdbuf);                 {remove comments to use}
  430.       called := TRUE                 {after compiling to memory}
  431.     end;
  432.  
  433.   while cmdbuf[i] = ' ' do             {skip leading blanks}
  434.     i := succ(i);
  435.   j := i;                     {point to start}
  436.   while (not(cmdbuf[i] = ' ') and (i <= length(cmdbuf))) do
  437.     begin
  438.       cmdbuf[i] := upcase(cmdbuf[i]);         {all commands upper case}
  439.       i := succ(i)                 {find end}
  440.     end;
  441.   getarg := copy(cmdbuf,j,i - j);         {assign return value}
  442.   j := i                     {new starting location}
  443. end;
  444. *)
  445.  
  446. function getarg(param : integer) : fnamestr;
  447. var i   : integer;
  448.     arg : string;
  449. begin
  450.   arg := ParamStr(param);
  451.   if (length(arg) >= 1) then
  452.     for i := 1 to length(arg) do
  453.       arg[i] := UpCase(arg[i]);
  454.   getarg := arg;
  455. end { of function getarg } ;
  456.  
  457.  
  458. {Add a new reference to the list of references, maintaining sorted order}
  459.  
  460. procedure addtolist(list : refptr; name : nametyp);
  461.  
  462. var current, newnode, last : refptr;
  463.  
  464. begin
  465.   current := list;
  466.   last := list;
  467.   while ((current^.next <> NIL) and (name > current^.name)) do
  468.     begin
  469.       last := current;
  470.       current := current^.next
  471.     end;
  472.   if name <> current^.name then
  473.     begin
  474.       getrefnode(newnode);
  475.       newnode^.name := name;
  476.       if name > current^.name then
  477.     current^.next := newnode
  478.     else begin
  479.       newnode^.next := current;
  480.       last^.next := newnode;
  481.       newnode^.name := name
  482.     end
  483.     end
  484.     else current^.number := succ(current^.number)
  485. end;
  486.  
  487. begin
  488.   HighVideo;
  489.   writeln;
  490.   writeln('REFS version ',VERSION);
  491.   writeln('Copyright 1985 by Ross A. Alford');
  492.     writeln('All commercial rights reserved');
  493.     writeln;
  494.     writeln('Revised version ', JVersion);
  495.     writeln('Revisions 1987, 1989 by Jeff MacKie-Mason');
  496.   writeln; NormVideo;
  497.   inittables;
  498.   ptr := 0;
  499.   year := '';
  500.   name := '';
  501.   comma := FALSE;
  502.   closeparen := FALSE;
  503.   infopen := FALSE;
  504.   outfopen := FALSE;
  505.   getsectnode(sector);
  506.   getrefnode(reflist);
  507.   infile := GetArg(1);
  508.   if infile = '' then msgexit('Input filename not specified');
  509.   assign(inf,infile);
  510. {$I-}
  511.   reset(inf);
  512. {$I+}
  513.   if ioresult <> 0 then msgexit('Input file not found');
  514.   infopen := TRUE;
  515.   numinfile := filesize(inf);
  516.   outfname := GetArg(2);
  517.   if outfname = '' then
  518.     begin
  519.       i := pos('.',infile) - 1;
  520.       if i = 0 then i := length(infile);
  521.       outfname := copy(infile,1,i);
  522.       outfname := outfname + '.REF';
  523.     end;
  524.   assign(outf,outfname);
  525.   if ((outfname <> 'CON') and (outfname <> 'LPT1')) then
  526.     begin
  527. {$I-}
  528.             reset(outf);
  529. {$I+}
  530.       if ioresult = 0 then msgexit('Output file exists--cannot overwrite')
  531.     end;
  532.   rewrite(outf);
  533.   outfopen := TRUE;
  534.   blockread(inf,sector^.data,1,result);
  535.   cursectnum := 1;
  536.   done := FALSE;
  537.   repeat
  538.     if nextbyte = ord('1') then            {CHECK FOR POTENTIAL DATE}
  539.       begin                       {add check for '2' in 1999}
  540.     year := chr(curbyte);               { :-) }
  541.     comma := false;
  542.     if number[nextbyte] then
  543.       begin
  544.         year := year + chr(curbyte);
  545.         if number[nextbyte] then
  546.           begin
  547.         year := year + chr(curbyte);
  548.         if (number[nextbyte] or letter[curbyte]) then
  549.           begin
  550.             year := year + chr(curbyte);
  551.             if (not number[nextbyte]) then
  552.               if lowcase[curbyte] then year := year + chr(curbyte);
  553.           end
  554.           end
  555.       end
  556.       end;
  557.     if (length(year) = 4) or (length(year) = 5) then
  558.       begin                    {got a date}
  559.     saveposition;
  560.     closeparen := FALSE;            {for later reference}
  561.     comma := FALSE;             {for later reference}
  562.     c := prevbyte;                {skip back four to avoid date}
  563.     c := prevbyte;
  564.     c := prevbyte;
  565.     c := prevbyte;
  566.     name := '';
  567.     xtra := '';
  568.  
  569. {*    Logic in this Repeat loop has been somewhat modified by JMM in order  *}
  570. {*    to catch two reference types in TeX files.  See notes in header.      *}
  571.     repeat
  572.       notfound := TRUE;
  573.       repeat
  574.         tempname := prevword
  575.         until ((tempname = '') or (length(tempname) > 1));
  576.       if ((hitnumber) and (name <> '')) then tempname := '';
  577.       if tempname[length(tempname) - 1] = chr(39) then   {fix posessives}
  578.         tempname := copy(tempname,1,length(tempname) - 2);
  579.       if tempname[length(tempname)] = chr(39) then     {fix other posessives}
  580.         tempname := copy(tempname,1,length(tempname) - 1);
  581.       if isupcase[ord(tempname[2])] then tempname := '';     {no abbrevs}
  582.       if ((tempname[1] = '-') or (tempname[length(tempname)] = '-'))
  583.         then tempname := '';   {no leading/trailing hyphens}
  584.         if (tempname <> '') and ((isupcase[ord(tempname[1])])
  585.                 or (Pos('\fnote{',tempname) = 1)
  586.                 or (Pos('\footnote{',tempname) = 1)) then begin
  587.             if Pos('\fnote{',tempname) = 1 then
  588.                 tempname := copy(tempname,8,length(tempname)-7);
  589.             if Pos('\footnote{',tempname) = 1 then
  590.                 tempname := copy(tempname,11,length(tempname)-10);
  591.             if ((name =  '') or (xtra <> '') or comma) then    begin
  592.                 name := tempname + ' ' + xtra + name;
  593.                 xtra := '';
  594.                 notfound := FALSE
  595.                 end
  596.             end
  597.         else if ((tempname = 'and')
  598.                 or (tempname = 'et')
  599.                 or (tempname = 'al}')
  600.                 or (tempname = 'al')) then
  601.             begin
  602.                 xtra := tempname + ' ' + xtra;
  603.                 notfound := FALSE
  604.                 end
  605.             else if ((tempname = '{\it') or (tempname = '{\em')) then
  606.                 notfound := FALSE;
  607.     until notfound;
  608.     if name <> '' then
  609.       begin
  610.         oldname := name;
  611.         oldyear := year;
  612.         name := name + year;
  613.         addtolist(reflist,name);
  614.         sinceref := 0
  615.       end;
  616.     restoreposition;
  617.     year := '';
  618.       end
  619.       else if sinceref < 4 then      {check for the Smith 1980a, b form}
  620.     if (lowcase[curbyte] and (length(oldyear) = 5))
  621.       then begin
  622.         if ((not letter[nextbyte]) and comma and (not closeparen)) then
  623.           begin
  624.         addtolist(reflist,oldname + copy(oldyear,1,4) + chr(prevbyte));
  625.         sinceref := 0
  626.           end
  627.           else begin
  628.         c := prevbyte;
  629.         sinceref := 10
  630.           end
  631.       end;
  632.     comma := (comma or (curbyte = ord(',')));
  633.     closeparen := (closeparen or (curbyte = ord(')')));
  634.     sinceref := succ(sinceref);
  635.   until done;
  636.   NormVideo;
  637.   writelist(outf,reflist);
  638. end.
  639.